package utils

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"golang.org/x/crypto/sha3"
	rand2 "math/rand"
	"regexp"
	"strings"
	"time"
	"unicode"
)

// 该函数用于生成包含数字和小写字母的随机字符串， length 参数是随机字符串的长度
func RandString(length int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	data := []byte(str)
	var result []byte
	// 创建独立随机源，避免了全局锁竞争问题
	r := rand2.New(rand2.NewSource(time.Now().UnixNano()))
	for i := 0; i < length; i++ {
		// r.Intn()生成随机索引
		result = append(result, data[r.Intn(len(data))])
	}
	return string(result)
}

// RandomNumber 该函数旨在生成一个指定位数的随机整数，例如 bit=3 时生成 100~999 之间的随机数。
func RandomNumber(bit int) int {
	minNum := intPow(10, bit-1)
	maxNum := intPow(10, bit) - 1
	// 使用 rand.NewSource 初始化随机种子。
	rand2.NewSource(time.Now().UnixNano())
	// 调用 rand.Intn 生成范围内的随机整数
	return rand2.Intn(maxNum-minNum+1) + minNum
}

// intPow 该函数通过循环累乘的方式实现了整数幂计算，输入两个整数 x（底数）和 y（指数），返回 x^y 的结果。例如：
// intPow(2, 3) // 返回 8（2^3）
// intPow(5, 0) // 返回 1（任何数的0次幂为1）
func intPow(x, y int) int {
	result := 1
	for i := 0; i < y; i++ {
		result *= x
	}
	return result
}

// Contains 该函数是判断切片中是否包含字段的元素
func Contains(slice []string, item string) bool {
	for _, e := range slice {
		if e == item {
			return true
		}
	}
	return false
}

// Stamp2str 函数用于将 Unix 时间戳（秒级）转换为格式化的日期时间字符串
func Stamp2str(timestamp int64) string {
	if timestamp == 0 {
		return ""
	}
	// 使用 time.Unix(timestamp, 0) 将秒级时间戳转换为 time.Time 类型，
	// 默认以时区解析时间。但通过 Format 方法输出时，实际会转换为系统本地时区的时间字符串（如 Asia/Shanghai）
	return time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")
}

// StampMill2str 函数用于将 Unix 时间戳（毫秒）转换为格式化的日期时间字符串
func StampMill2str(timestamp int64) string {
	if timestamp == 0 {
		return ""
	}
	// 使用 time.UnixMilli(timestamp) 将毫秒级时间戳转换为 time.Time 类型，
	// 默认以时区解析时间。但通过 Format 方法输出时，实际会转换为系统本地时区的时间字符串（如 Asia/Shanghai）
	return time.UnixMilli(timestamp).Format("2006-01-02 15:04:05")
}

// Str2stamp 函数用于将时间字符串转换为 Unix 时间戳（秒级）
func Str2stamp(str string) int64 {
	if len(str) == 0 {
		return 0
	}
	layout := "2006-01-02 15:04:05"
	// 解析时间字符串，将结果视为本地时区时间。例如：
	// 若本地时区为 Asia/Shanghai，字符串 "2023-09-01 12:00:00" 会被解析为东八区时间，对应 UTC 时间 04:00:00
	t, err := time.ParseInLocation(layout, str, time.Local)
	if err != nil {
		return 0
	}
	// 返回时间戳，单位秒
	return t.Unix()
}

// GenPassword 生成密码
func GenPassword(pass string, salt string) string {
	// 将密码 pass 和盐值 salt 进行字符串拼接（pass + salt），并转换为字节数组 data。这种组合方式通过引入盐值增强了密码的随机性
	data := []byte(pass + salt)
	// 使用 SHA3-256 算法对 data 进行单向散列加密。SHA3-256 是第三代安全哈希算法，输出固定 256 位（32 字节）的哈希值，具有抗碰撞性和抗预映射性
	hash := sha3.Sum256(data)
	// 将二进制哈希值转换为 64 位十六进制字符串（%x 格式化符），便于存储和传输。
	return fmt.Sprintf("%x", hash)
}

// JsonEncode json 编码
func JsonEncode(value interface{}) string {
	bytes, err := json.Marshal(value)
	if err != nil {
		return ""
	}
	return string(bytes)
}

// JsonDecode Json解码
func JsonDecode(src string, dest interface{}) error {
	return json.Unmarshal([]byte(src), dest)
}

// InterfaceToString 把传递进来的数据转成string返回
func InterfaceToString(value interface{}) string {
	if str, ok := value.(string); ok {
		return str
	}
	return JsonEncode(value)
}

// CutWords 裁剪单词
func CutWords(str string, num int) string {
	// 按照空格分割字符串为单词切片
	// 自动忽略开头和结尾的空白：例如 " hello world " 会被分割为 ["hello", "world"]
	words := strings.Fields(str)
	// 如果单词数量超过指定数量，则裁剪单词，否则保持原样
	if len(words) > num {
		return strings.Join(words[:num], " ") + " ..."
	} else {
		return str
	}
}

// HasChinese 判断文本中是否包含中文
func HasChinese(text string) bool {
	for _, char := range text {
		if unicode.Is(unicode.Scripts["Han"], char) {
			return true
		}
	}
	return false
}

// GenRedeemCode 生成固定长度的随机十六进制字符串
func GenRedeemCode(codeLength int) (string, error) {
	bytes := make([]byte, codeLength/2)
	// 通过 rand.Read(bytes) 从密码学安全的随机数生成器（crypto/rand 包）获取随机字节
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	// 使用 hex.EncodeToString 将二进制数据转换为十六进制字符串
	return hex.EncodeToString(bytes), nil
}

// IsValidEmail 检查给定的字符串是否是有效的电子邮件地址
func IsValidEmail(email string) bool {
	// 这个正则表达式匹配大多数常见的邮箱格式
	emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	return emailRegex.MatchString(email)
}

// IsValidMobile 检查给定的字符串是否是有效的中国大陆手机号
func IsValidMobile(phone string) bool {
	// 支持 13x、14x、15x、16x、17x、18x、19x 开头的号码
	phoneRegex := regexp.MustCompile(`^1[3-9]\d{9}`)
	return phoneRegex.MatchString(phone)
}
